﻿#pragma once
#include "taocommonglobal.h"
#include "threadcommon.h"
#include <QMap>
#include <QThread>
namespace TaoCommon
{
    // 对Qt Worker-Controller 线程模型的简单封装，适合精确控制的任务。
    // 用法: ThreadController::getInstance()->work(workCall, resultCall)
    // workCall是在新线程中执行的任务，resultCall是任务执行完成后，回到调用线程中用来处理执行结果。
	// work函数返回值为任务id。
    // 取消任务用ThreadController::getInstance()->cancle(id)函数。
    class ThreadWorker : public QObject
    {
        Q_OBJECT
    public:
        ThreadWorker(uint64_t id, const WorkCallback &workCall, QObject *parent = nullptr) : QObject(parent), m_id(id), m_workCall(workCall) {}

    signals:
        void workFinished(bool, uint64_t);
    public slots:
        void doWork()
        {
            bool ret = m_workCall();
            emit workFinished(ret, m_id);
        }
    private:
        const uint64_t m_id;
        WorkCallback m_workCall;
    };
    class TAO_API ThreadController : public QObject
    {
        Q_OBJECT
    public:
        static ThreadController *getInstance()
        {
            static ThreadController controller;
            return &controller;
        }

        ~ThreadController()
        {
            for (auto k : m_threadMap.uniqueKeys())
            {
                if (m_threadMap.value(k)->isRunning())
                {
                    m_threadMap.value(k)->quit();
                    m_threadMap.value(k)->wait();
                }
            }
            qDeleteAll(m_threadMap);
            m_threadMap.clear();
            m_resultCallback.clear();
        }
        uint64_t work(const WorkCallback &workCall, const WorkResultCallback &resultCall)
        {
            QThread *thread = new QThread;
            m_rollId++;
            ThreadWorker *obj = new ThreadWorker(m_rollId, workCall);
            m_threadMap[m_rollId] = thread;
            m_resultCallback[m_rollId] = resultCall;
            obj->moveToThread(thread);

            connect(thread, &QThread::finished, obj, &QObject::deleteLater);
            connect(thread, &QThread::started, obj, &ThreadWorker::doWork);
            connect(obj, &ThreadWorker::workFinished, this, &ThreadController::onWorkFinished, Qt::QueuedConnection);
            thread->start();
            return m_rollId;
        }
        void cancle(uint64_t id)
        {
            auto it = m_threadMap.find(id);
            if (it != m_threadMap.end())
            {
                if ((*it)->isRunning())
                {
                    (*it)->terminate();
                }
            }
            m_resultCallback.remove(id);
        }
        void quit(uint64_t id)
        {
            auto it = m_threadMap.find(id);
            if (it != m_threadMap.end())
            {
                if ((*it)->isRunning())
                {
                    (*it)->quit();
                    (*it)->wait();
                    (*it)->deleteLater();
                }
            }
            m_resultCallback.remove(id);
        }
        QList<uint64_t> getAllWorkId() const 
        {
            return m_threadMap.uniqueKeys();
        }
    protected slots :
        void onWorkFinished(bool ok, uint64_t id) 
        {
            auto it = m_threadMap.find(id);
            if (it != m_threadMap.end())
            {
                if ((*it)->isRunning())
                {
                    (*it)->quit();
                    (*it)->wait();
                    (*it)->deleteLater();
                }
                m_threadMap.remove(id);
            }
            auto caller = m_resultCallback.find(id);
            if (caller != m_resultCallback.end())
            {
                (*caller)(ok);
                m_resultCallback.remove(id);
            }
        }
    protected:
        ThreadController(QObject *parent = nullptr) : QObject(parent) {}

    private:
        uint64_t m_rollId = 0;
        QMap<uint64_t, QThread *> m_threadMap;
        QMap<uint64_t, WorkResultCallback> m_resultCallback;
    };
} // namespace LCIM
